package qinggui.mlz.console;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import qinggui.mlz.console.annotations.VerifiedUser;
import qinggui.mlz.module.user.entity.User;
import qinggui.mlz.module.user.service.BaseUserService;
import qinggui.mlz.module.utils.BaseUtils;
import qinggui.mlz.module.utils.SignUtils;
import qinggui.mlz.module.utils.SpringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.math.BigInteger;

@Slf4j
public class UserAuthorityResolver implements HandlerMethodArgumentResolver {

    @Autowired
    private BaseUserService userService;
    // 是否检查用户权限的标志位
    private boolean isCheckAuthority;

    /**
     * 构造函数：通过启动参数初始化权限检查开关
     * @param appArguments Spring启动参数
     */
    public UserAuthorityResolver(ApplicationArguments appArguments) {
        String[] arguments = appArguments.getSourceArgs();
        // 如果启动参数不足3个，默认开启权限检查
        if (arguments == null || arguments.length <= 3) {
            isCheckAuthority = true;
            return;
        }
        // 从第三个参数获取是否模拟登录的标志
        String isMockUserLogin = arguments[2];
        // 参数为空时默认开启检查
        if (BaseUtils.isEmpty(isMockUserLogin)) {
            isCheckAuthority = true;
        } else {
            // 解析布尔值
            isCheckAuthority = Boolean.parseBoolean(isMockUserLogin);
        }
        log.info("Check user authority: {}", Boolean.toString(isCheckAuthority));
    }

    /**
     * 判断是否支持当前参数解析
     * @param parameter 方法参数
     * @return 仅处理带有@VerifiedUser注解的User类型参数
     */
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        Class<?> type = parameter.getParameterType();
        return type.isAssignableFrom(User.class) && parameter.hasParameterAnnotation(VerifiedUser.class);
    }

    /**
     * 解析参数
     * @param parameter 方法参数
     * @param container 模型和视图容器
     * @param request 请求对象
     * @param factory 数据绑定工厂
     * @return
     */
    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer container,
                                  NativeWebRequest request,
                                  WebDataBinderFactory factory) {
        // 如果启用权限检查
        if (isCheckAuthority) {
            // 判断是否为APP端请求
            String isAppS = SpringUtils.getProperty("application.isapp");
            boolean isApp = isAppS.equals("1") ? true : false;
            // 获取原生HttpServletRequest
            HttpServletRequest sRequest = (HttpServletRequest) request.getNativeRequest();
            // APP端处理逻辑（通过签名验证）
            if (isApp) {
                // 获取签名的key
                String signKey = SpringUtils.getProperty("application.sign.key");
                // 根据key值获取请求头中的签名
                String sign = sRequest.getHeader(signKey);
                if (!BaseUtils.isEmpty(sign)) {
                    BigInteger userId = SignUtils.parseSign(sign);
                    log.info("userId: {}, sign: {}", userId, sign);
                    if (!BaseUtils.isEmpty(userId)) {
                        return userService.getById(userId);
                    }
                }
                // 签名验证失败
                return null;
            } else {
                // WEB端处理逻辑（通过Session验证）
                // 获取现有Session（不创建新Session）
                HttpSession session = sRequest.getSession(false);
                if (BaseUtils.isEmpty(session)) {
                    return null;
                }
                String signKey = SpringUtils.getProperty("application.session.key");
                Object value = session.getAttribute(signKey);
                if (value == null) {
                    return null;
                }

                String sValue = (String) value;
                return JSON.parseObject(sValue, User.class);
            }
        }
        return userService.getById(BigInteger.valueOf(1));
    }
}
